/*
  Dependent upon util
*/

var MONTH_LIST = ['January', 'February', 'March', 'April', 'May', 'June',
    'July','August','September','October','November','December'];

function DateChooser(elementId, opt_startDate, opt_minDate, opt_maxDate,
    opt_highlightDate) {
  this.elementId = elementId;
  this.startDate = false;
  this.monthYear = false;
  this.minDate = opt_minDate;
  this.maxDate = opt_maxDate;
  this.highlightDate = opt_highlightDate;
  this.monthChange = false;

  var element = $(this.elementId);
  if (element.setAttribute) {
    element.setAttribute('autocomplete', 'off');
  }
  element.onfocus = this.handleFocus.bindAsEventListener(this);
  element.onblur = this.handleBlur.bindAsEventListener(this);
}

/*
  ------------------------------------------------------------------------------
  Event Handlers
  ------------------------------------------------------------------------------
*/

/*
  onfocus handler for the form element.
*/
DateChooser.prototype.handleFocus = function() {
  this.show();
};

/*
  onblur handler for the form element.
*/
DateChooser.prototype.handleBlur = function() {
  if (!this.monthChange) {
    this.select();
  }
};

DateChooser.prototype.handleMouseOut = function() {
  this.value = null;
};

DateChooser.prototype.handleTitleMouseOver = function(event) {
  var element = util.getEventTarget(event);
  element.className = element.className.replace('-hover', '');
  element.className += '-hover';
  this.monthChange = true;
};

DateChooser.prototype.handleTitleMouseOut = function(event) {
  var element = util.getEventTarget(event);
  element.className = element.className.replace('-hover', '');
  this.monthChange = false;
};

DateChooser.prototype.handlePreviousClick = function(event) {
  this.changeMonth(-1);
  this.monthChange = false;
};

DateChooser.prototype.handleNextClick = function(event) {
  this.changeMonth(1);
  this.monthChange = false;
};

DateChooser.prototype.handleDayMouseOver = function(event, date) {
  var element = util.getEventTarget(event);
  element.className = element.className.replace('-hover', '');
  element.className += '-hover';
  this.value = date;
};

DateChooser.prototype.handleDayMouseOut = function(event) {
  var element = util.getEventTarget(event);
  element.className = element.className.replace('-hover', '');
};

/*
  ------------------------------------------------------------------------------
  Building the UI
  ------------------------------------------------------------------------------
*/

DateChooser.prototype.show = function() {
  
  if (!this.monthYear) {
    this.calcMonthYear();
  }
  
  /* Get the position of the input element */
  var pos = util.findPos($(this.elementId));
  var x = pos[0];
  var y = pos[1] + $(this.elementId).offsetHeight - 1;

  /* Create/find a div to contain the date chooser */
  var currDiv = $(this.elementId + '-datechooser');
  if (currDiv) {
    var mainDiv = currDiv;
    mainDiv.innerHTML = '';
  } else {
    var mainDiv = document.createElement('div');
    mainDiv.id = this.elementId + '-datechooser';
  }
  mainDiv.onmouseout = this.handleMouseOut.bind(this);
  mainDiv.className = 'datechooser-main';
  mainDiv.style.left = x + 'px';
  mainDiv.style.top = y + 'px';

  /* Create the month/year selector */
  var navTable = this.buildNavTable();
  mainDiv.appendChild(navTable);

  /* Create the calendar */
  var mainTable = this.buildMainTable();
  mainDiv.appendChild(mainTable);

  /* Append the whole thing to the dom */
  document.getElementsByTagName('body')[0].appendChild(mainDiv);
};

DateChooser.prototype.buildNavTable = function() {
  var navTable = document.createElement('table');
  navTable.className = 'datechooser-navtable';
  navTable.cellSpacing = '0px';
  navTable.cellPadding = '0px';

  var tbody = document.createElement('tbody');
  navTable.appendChild(tbody);

  var row = document.createElement('tr');
  tbody.appendChild(row);

  var leftCell = document.createElement('td');
  leftCell.className = 'datechooser-previous';
  leftCell.innerHTML = '<<';
  leftCell.onmouseover = this.handleTitleMouseOver.bindAsEventListener(this);
  leftCell.onmouseout = this.handleTitleMouseOut.bindAsEventListener(this);
  leftCell.onclick = this.handlePreviousClick.bindAsEventListener(this);
  row.appendChild(leftCell);

  var middleCell = document.createElement('td');
  middleCell.innerHTML = MONTH_LIST[this.monthYear.getMonth()].substring(0, 3) +
      ' ' + this.monthYear.getYear();
  middleCell.className = 'datechooser-title';
  row.appendChild(middleCell);

  var rightCell = document.createElement('td');
  rightCell.innerHTML = '>>';
  rightCell.className = 'datechooser-next';
  rightCell.onmouseover = this.handleTitleMouseOver.bindAsEventListener(this);
  rightCell.onmouseout = this.handleTitleMouseOut.bindAsEventListener(this);
  rightCell.onclick = this.handleNextClick.bindAsEventListener(this);
  row.appendChild(rightCell);
  
  return navTable;
};

DateChooser.prototype.buildMainTable = function() {
  /* Create and fill the calendar */
  var mainTable = document.createElement('table');
  mainTable.className = 'datechooser-maintable';
  mainTable.cellSpacing = '0px';
  mainTable.cellPadding = '0px';
  var tbody = document.createElement('tbody');

  /* Label the days of the week */
  var daysRow = document.createElement('tr');
  var days = ['S', 'M', 'T', 'W', 'R', 'F', 'S'];
  for (var i = 0; i < days.length; i++) {
    var cell = document.createElement('th');
    cell.innerHTML = days[i];
    daysRow.appendChild(cell);
  }
  tbody.appendChild(daysRow);

  /* Generate the calendar */
  var currDate = this.getFirstDay();
  for (var week = 0; week < 5; week++) {
    var calRow = document.createElement('tr');
    for (var day = 0; day < 7; day++) {
      var cell = document.createElement('td');
      cell.innerHTML = currDate.getDate();
      cell.className = 'datechooser-days-normal';
      var currDateValue = this.getFormattedDate(currDate); 
      cell.onmouseover = this.handleDayMouseOver.bindAsEventListener(this,
          currDateValue);
      cell.onmouseout = this.handleDayMouseOut.bindAsEventListener(this);

      /* Separate style for previous and following months */
      if (currDate.getMonth() != this.monthYear.getMonth()) {
        cell.className = 'datechooser-days-prevnext';
      }
      
      /* Separate style for today */
      if (this.getNumericDate(currDate) == this.getNumericDate(new Date())) {
        cell.className = 'datechooser-days-today';
      }

      var belowMin = this.minDate && this.getNumericDate(currDate) <
          this.getNumericDate(this.minDate);
      var aboveMax = this.maxDate && this.getNumericDate(currDate) >
          this.getNumericDate(this.minDate);
      if (belowMin || aboveMax) {
        cell.className = 'datechooser-days-outofrange';
        cell.onmouseover = null;
        cell.onmouseout = null;
      }

      if (this.highlightDate && this.getNumericDate(currDate) ==
          this.getNumericDate(this.highlightDate)) {
        cell.className = 'datechooser-days-highlight';
      }

      calRow.appendChild(cell);
      currDate.setDate(currDate.getDate() + 1);
    }
    tbody.appendChild(calRow);
  }
  mainTable.appendChild(tbody);
  return mainTable;
};

/*
  ------------------------------------------------------------------------------
  Date Calculations
  ------------------------------------------------------------------------------
*/

/*
  Return a date object corresponding to midnight before the first day of the
  week which contains the first day of the current month and year.
  (e.g. for February 2008 the first date on the calendar is Jan 27th)
*/
DateChooser.prototype.getFirstDay = function() {
  var date = new Date(this.monthYear.getYear(), this.monthYear.getMonth(), 1);
  date.setDate(date.getDate() - date.getDay());
  date.setHours(0);
  date.setMinutes(0);
  date.setSeconds(0);
  date.setMilliseconds(0);
  return date;
};

/*
  Determines what month should be displayed and then pops up the calendar.
  There are three possible sources for this date (in order of precedence):
    - The date currently in the form field
    - The startDate specified during construction
    - Today's date.
*/
DateChooser.prototype.calcMonthYear = function() {
  var currValue = $(this.elementId).value;
  if (currValue) {
    try {
      var pieces = currValue.split('-');
      var month = parseInt(pieces[1], 10) - 1;
      var year = parseInt(pieces[0], 10);
      this.monthYear = new MonthYear(month, year);
    } catch (ex) {
      this.monthYear = false;
    }
  }
  if (!this.monthYear && this.startDate) {
    var month = this.startDate.getMonth();
    var year = this.startDate.getFullYear();
    this.monthYear = new MonthYear(month, year);
  }
  if (!this.monthYear) {
    var month = (new Date()).getMonth();
    var year = (new Date()).getFullYear();
    this.monthYear = new MonthYear(month, year);
  }
};

DateChooser.prototype.getNumericDate = function(dateObj) {
  var year = dateObj.getFullYear();
  var month = dateObj.getMonth() + 1;
  var date = dateObj.getDate();
  return year * 10000 + month * 100 + date;
};

DateChooser.prototype.getFormattedDate = function(dateObj) {
  var d = '' + this.getNumericDate(dateObj);
  return [d.substring(0, 4), d.substring(4, 6), d.substring(6, 8)].join('-');
};

DateChooser.prototype.select = function () {
  if (this.value) {
    $(this.elementId).value = this.value;
  }
  var objRegExp = /^\d{4}-\d{2}-\d{2}$/
  // If the date isn't in the correct format, kill it.
  if (!objRegExp.test($(this.elementId).value)) {
    $(this.elementId).value = '';
  }
  this.close();
};

DateChooser.prototype.close = function() {
  this.monthYear = false;
  var mainDiv = $(this.elementId + '-datechooser');
  if (mainDiv.parentNode) {
    mainDiv.parentNode.removeChild(mainDiv);
  }
};

DateChooser.prototype.changeMonth = function (direction) {
  this.monthYear.incrementMonth(direction);
  this.show();
  $(this.elementId).focus();
};

/*
  ------------------------------------------------------------------------------
  MonthYear Class
  
  Represents a Month/Year combination. Essentially a simplified date object that
  lets us avoid a lot of unnecessary pain.
  ------------------------------------------------------------------------------
*/

function MonthYear(month, year) {
  this.month = month;
  this.year = year;
}

MonthYear.prototype.getMonth = function() {
  return this.month;
};

MonthYear.prototype.getYear = function() {
  return this.year;
};

MonthYear.prototype.incrementMonth = function(increment) {
  this.month += increment;
  if (this.month > 11) {
    this.month -= 12;
    this.year++;
  }
  if (this.month < 0) {
    this.month += 12;
    this.year--;
  }
};
